#!/usr/bin/env python3

import sys

class NotValidClassException(Exception):
    pass


_flag_dict = {
    0x0001: 'ACC_PUBLIC', 0x0002: 'ACC_PRIVATE',       0x0004: 'ACC_PROTECTED', 0x0008: 'ACC_STATIC',
    0x0010: 'ACC_FINAL',  0x0020: 'ACC_SYNCHRONIZED',  0x0040: 'ACC_BRIDGE',    0x0080: 'ACC_VARARGS',
    0x0100: 'ACC_NATIVE', 0x0400: 'ACC_ABSTRACT',      0x0800: 'ACC_STRICT',    0x1000: 'ACC_SYNTHETIC',
    0x4000: 'ACC_ENUM'
}

class Attributes:
    def __init__(self, attribute_name_index, attribute_length, bytes):
        self.attribute_name_index = attribute_name_index
        self.attribute_length = attribute_length
        self.bytes = bytes
        self._position = 0
        self.constant_pools = None

    def parse_uint(self, byte=2):
        next_position = self._position + byte
        result = int.from_bytes(self.bytes[self._position:next_position], byteorder='big')
        self._position = next_position
        return result

    def parse_u16int(self):
        return self.parse_uint()


    def to_string(self, pools):
        return 'Not Implemented: ' + pools[self.attribute_name_index - 1].info

    @staticmethod
    def generate_attribute(java_class):
        attribute_name_index = java_class.parse_u16int()
        next_position = java_class._position + 4
        attribute_length = int.from_bytes(java_class.bytes[java_class._position: next_position], byteorder='big')
        java_class._position = next_position
        
        next_position = java_class._position + attribute_length
        info_bytes = java_class.bytes[java_class._position : next_position]
        java_class._position = next_position

        attributes = [AttributeConstantValue, AttributeCode, AttributeStackMapTable, AttributeExceptions,
                      AttributeBoostrapMethods, AttributeInnerClasses, AttributeEnclosingMethod, AttributeSynthetic,
                      AttributeSignature, AttributeRuntimeVisibleAnnotations, AttributeRuntimeInvisibleAnnotations, AttributeRuntimeVisibleParameterAnnotations,
                      AttributeRuntimeInvisibleParameterAnnotations, AttributeRuntimeVisibleTypeAnnotations, AttributeRuntimeInVisibleTypeAnnotations, AttributeAnnotationDefault,
                      AttributeMethodParameters, AttributeSourceFile, AttributeSourceDebugExtension, AttributeLineNumberTable,
                      AttributeLocalVariableTable, AttributeLocalVariableTypeTable, AttributeDeprecated]
        attribute_name = java_class.constant_pools[attribute_name_index - 1].info
        target_type = None
        for item in attributes:
            if item.name == attribute_name:
                target_type = item
                break
        else:
            raise Exception('not found valid attribute type: ' + attribute_name)
        result = target_type(attribute_name_index, attribute_length, info_bytes)
        result.constant_pools = java_class.constant_pools
        if hasattr(result, 'parse_info') and callable(result.parse_info):
            result.parse_info()
        result.constant_pools = None
        return result


class Field:
    @staticmethod
    def generate_field(java_class):
        result = Field()
        result.parse_info(java_class)
        return result
    
    def parse_info(self, java_class):
        self.access_flags = java_class.parse_u16int()
        self.name_index = java_class.parse_u16int()
        self.descriptor_index = java_class.parse_u16int()
        self.attributes_count = java_class.parse_u16int()
        self.attributes = [Attributes.generate_attribute(java_class) for index in range(self.attributes_count)]

    def to_string(self, pools=None):
        if pools is None:
            return f"access_flags={self.access_flags}, name_index={self.name_index}" \
                + f",descriptor_index={self.descriptor_index}, attributes_count={self.attributes_count}"

        flags = ''
        for flag in _flag_dict:
            if flag & self.access_flags != 0:
                flags += _flag_dict[flag] + ' '
        name = pools[self.name_index - 1].info
        descriptor = pools[self.descriptor_index - 1].info
        result = name.rjust(16) + ": " + descriptor + " (" + flags  + ")"
        for attrib in self.attributes:
            result += '\n' + attrib.to_string(pools)
        return result


class Method:
    @staticmethod
    def generate_method(java_class):
        result = Method()
        result.parse_info(java_class)
        return result
    
    def parse_info(self, java_class):
        self.access_flags = java_class.parse_u16int()
        self.name_index = java_class.parse_u16int()
        self.descriptor_index = java_class.parse_u16int()
        self.attributes_count = java_class.parse_u16int()
        self.attributes = [Attributes.generate_attribute(java_class)]

    def to_string(self, pools=None):
        flags = ''
        for flag in _flag_dict:
            if flag & self.access_flags != 0:
                flags += _flag_dict[flag]
        name = pools[self.name_index - 1].info
        descriptor = pools[self.descriptor_index - 1].info
        result = "".rjust(10) + name + ":" + descriptor + "\t\t" + flags
        for attrib in self.attributes:
            result += '\n' + attrib.to_string(pools)
        return result


class ConstantItem:
    def parse_info(self, bytes, start_index):
        """parse info accounding to tag type, and Return new index"""
        raise NotImplementedError("not implemented error")

    def __str__(self):
        return self.to_string()

    def __repr__(self):
        return self.__str__()

    def to_string(self, constant_pools=None):
        """Return String Presentation"""
        result =  f"{self.__class__.__name__[8:]:19} {self.value_str():40}"
        if constant_pools:
            comment = self.comment(constant_pools)
            if comment:
                result += '// ' + comment
        return result        

    def value_str(self):
        return "Not Implemented"

    def comment(self, pools):
        return None

    @staticmethod
    def constant_item(tag_byte):
        childs = [ConstantClass, ConstantFieldRef, ConstantMethodRef,
                  ConstantInterfaceMethodRef, ConstantString, ConstantInteger,
                  ConstantFloat, ConstantLong, ConstantDouble,
                  ConstantNameAndType, ConstantUtf8, ConstantMethodHandle,
                  ConstantMethodType, ConstantInvokeDynamic]
        tag_2_child = {item.tag: item for item in childs}
        child = tag_2_child[tag_byte]
        return child()

class ConstantClass(ConstantItem):
    tag = 7
    def parse_info(self, bytes, start_index):
        end_index = start_index + 2
        self.name_index = int.from_bytes(bytes[start_index:end_index], byteorder='big')
        return end_index

    def name(self, pools):
        return pools[self.name_index - 1].info

    def value_str(self):
        return f"#{self.name_index}"

    def comment(self, pools):
        return pools[self.name_index - 1].info

class ConstantItemWithClassIndex(ConstantItem):

    def parse_info(self, bytes, start_index):
        end_index = start_index + 2
        self.class_index = int.from_bytes(bytes[start_index:end_index], byteorder='big')
        start_index = end_index
        end_index += 2
        self.name_and_type_index = int.from_bytes(bytes[start_index:end_index], byteorder='big')
        return end_index

    def value_str(self):
        return f"#{self.class_index}.#{self.name_and_type_index}"

    def comment(self, pools):
        class_name = pools[self.class_index - 1].name(pools)
        name_type = pools[self.name_and_type_index - 1].name_and_type(pools)
        return class_name + "." + name_type            

class ConstantFieldRef(ConstantItemWithClassIndex):
    tag = 9

class ConstantMethodRef(ConstantItemWithClassIndex):
    tag = 10

class ConstantInterfaceMethodRef(ConstantItemWithClassIndex):
    tag = 11

class ConstantString(ConstantItem):
    tag = 8
    def parse_info(self, bytes, start_index):
        end_index = start_index + 2
        self.string_index = int.from_bytes(bytes[start_index:end_index], byteorder='big')
        return end_index

    def value_str(self):
        return f"#{self.string_index}"

    def comment(self,pools):
        return pools[self.string_index - 1].info

class ConstantInteger(ConstantItem):
    tag = 3
    def parse_info(self, bytes, start_index):
        end_index = start_index + 4
        self.value = int.from_bytes(bytes[start_index:end_index], byteorder='big')
        return end_index

class ConstantFloat(ConstantItem):
    tag = 4
    def parse_info(self, bytes, start_index):
        end_index = start_index + 4
        self.value = bytes[start_index:end_index]
        return end_index

class ConstantLong(ConstantItem):
    tag = 5
    def parse_info(self, bytes, start_index):
        return start_index + 8

class ConstantDouble(ConstantItem):
    tag = 6
    def parse_info(self, bytes, start_index):
        return start_index + 8

class ConstantNameAndType(ConstantItem):
    tag = 12
    def parse_info(self, bytes, start_index):
        end_index = start_index + 2
        self.name_index = int.from_bytes(bytes[start_index:end_index], byteorder='big')
        start_index = end_index
        end_index += 2
        self.descriptor_index = int.from_bytes(bytes[start_index:end_index], byteorder='big')
        return end_index

    def name_and_type(self, pools):
        if pools is None:
            return ''
        name = pools[self.name_index - 1].info
        descriptor = pools[self.descriptor_index - 1].info
        if descriptor.startswith('('):
            return name + descriptor
        else:
            return name + ':' + descriptor

    def value_str(self):
        return f"#{self.name_index} #{self.descriptor_index}"

    def comment(self, pools):
        return self.name_and_type(pools)

class ConstantUtf8(ConstantItem):
    tag = 1
    def parse_info(self, bytes, start_index):
        end_index = start_index + 2
        self.length = int.from_bytes(bytes[start_index:end_index], byteorder='big')
        start_index = end_index
        end_index = start_index + self.length
        value_bytes = bytes[start_index:end_index]
        self.info = self.parse_to_str(value_bytes)
        return end_index

    def value_str(self):
        return self.info

    def parse_to_str(self, bytes):
        current_index = 0
        length = len(bytes)
        result = []
        while current_index < length:
            first_byte = bytes[current_index]
            if first_byte == 0xED:
                # case u, v, w, x, y
                u = first_byte
                v = bytes[current_index + 1]
                w = bytes[current_index + 2]
                x = bytes[current_index + 3]
                y = bytes[current_index + 4]
                value = 0x10000 + ((v & 0x0f) << 16) + ((w & 0x3f) << 10) + ((y & 0x0f) << 6) + (z & 0x3f)
                result.append(value)
                current_index += 5
            elif first_byte & 0xF0 == 0xE0:
                # case x, y, z
                x = first_byte
                y = bytes[current_index + 1]
                z = bytes[current_index + 2]
                value = ((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f)
                result.append(value)
                current_index += 3
            elif first_byte & 0xE0 == 0xC0:
                # case x, y
                x = first_byte
                y = bytes[current_index + 1]
                value = ((x & 0x1f) << 6) + (y & 0x3f)
                result.append(value)
                current_index += 2
            elif first_byte & 0x80 == 0:
                # case ASCII
                result.append(chr(first_byte))
                current_index += 1
            else:
                raise Exception('非法状态')
        return ''.join(result)

class ConstantMethodHandle(ConstantItem):
    tag = 15
    def parse_info(self, bytes, start_index):
        self.reference_kind = bytes[start_index]
        self.reference_index = int.from_bytes(bytes[start_index + 1: start_index + 3], byteorder='big')
        return start_index + 3

    def value_str(self):
        return f"#{self.reference_kind}:#{self.reference_index}"

    def comment(self, pools):
        kind_2_desc = {
            1: 'GETFIELD', 2: 'GETSTATIC', 3: 'PUTFIELD', 4: 'PUTSTATIC',
            5: 'INVOKEVIRTUAL', 6: 'INVOKESTATIC', 7: 'INVOKESPECIAL',
            8: 'NEWINVOKESPECIAL', 9: 'INVOKEINTERFACE'
        }
        return kind_2_desc[self.reference_kind] + ' '  + pools[self.reference_index - 1].comment(pools)

class ConstantMethodType(ConstantItem):
    tag = 16
    def parse_info(self, bytes, start_index):
        self.descriptor_index = int.from_bytes(bytes[start_index : start_index + 2], byteorder='big')
        return start_index + 2

    def value_str(self):
        return f"#{self.descriptor_index}"

    def comment(self, pools):
        return pools[self.descriptor_index - 1].info

class ConstantInvokeDynamic(ConstantItem):
    tag = 18
    def parse_info(self, bytes, start_index):
        self.bootstrap_method_attr_index = int.from_bytes(bytes[start_index:start_index + 2], byteorder='big')
        self.name_and_type_index = int.from_bytes(bytes[start_index + 2 : start_index + 4], byteorder='big')
        return start_index + 4

    def value_str(self):
        return f"#{self.bootstrap_method_attr_index}:#{self.name_and_type_index}"

    def comment(self, pools):
        return '#' + str(self.bootstrap_method_attr_index) + ':' + pools[self.name_and_type_index - 1].comment(pools)


class JavaClassFile:
    def __init__(self):
        self._position = 0
        self.bytes = None

    def parse_bytes(self, bytes):
        self.bytes = bytes
        self._position = 0
        self.magic = int.from_bytes(self.bytes[:4], byteorder='big')
        if self.magic != 0xCAFEBABE:
            raise NotValidClassException()
        self._position = 4
        self.minor_version = self.parse_u16int()
        self.major_version = self.parse_u16int()
        self.constant_pool_count = self.parse_u16int()
        self.constant_pools = []
        for index in range(self.constant_pool_count - 1):
            tag = self.bytes[self._position]
            item = ConstantItem.constant_item(tag)
            self._position += 1
            self._position = item.parse_info(self.bytes, self._position)
            self.constant_pools.append(item)
        self.access_flags = self.parse_u16int()
        self.this_class_index = self.parse_u16int()
        self.super_class_index = self.parse_u16int()
        self.interfaces_count = self.parse_u16int()
        self.interface_indexs = [self.parse_u16int() for item in range(self.interfaces_count)]
        self.fields_count = self.parse_u16int()
        self.fields = [ Field.generate_field(self) for index in range(self.fields_count)]
        self.methods_count = self.parse_u16int()
        self.methods = [Method.generate_method(self) for index in range(self.methods_count) ]
        self.attributes_count = self.parse_u16int()
        self.attributes = [Attributes.generate_attribute(self) for index in range(self.attributes_count)]
        

    def parse_u16int(self):
        next_position = self._position + 2
        result = int.from_bytes(self.bytes[self._position: next_position], byteorder='big')
        self._position = next_position
        return result
        

    def print(self):
        flags_dict = {
            0x0001: 'ACC_PUBLIC',
            0x0010: 'ACC_FINAL',
            0x0020: 'ACC_SUPER',
            0x0200: 'ACC_INTERFACE',
            0x0400: 'ACC_ABSTRACT',
            0x1000: 'ACC_SYNTHETIC',
            0x4000: 'ACC_ENUM'
        }
        flags = ''
        for flag in flags_dict:
            if self.access_flags & flag != 0:
                flags += flags_dict[flag] + ' '

        this_class = self.constant_pools[self.this_class_index - 1].name(self.constant_pools)
        super_class = self.constant_pools[self.super_class_index - 1].name(self.constant_pools)

        interfaces = ''
        for index in self.interface_indexs:
            interfaces += self.constant_pools[index - 1].name(self.constant_pools)
        
        format = lambda left,value: f"{left.rjust(23)}: {value}\n"
        print('')
        print(format('class version', str(self.major_version) + '.' + str(self.minor_version)),
              format('Constant_Pool_Count', self.constant_pool_count),
              format('this_class', this_class),
              format('super_class', super_class),
              format('interfaces', interfaces),
              format('access_flags', flags),
              format('attributes_count', self.attributes_count),
              sep='')
        
        print('Constant Pools:')
        for idx, val in enumerate(self.constant_pools):
            print(('#' + str(idx + 1) + ' =').rjust(8), val.to_string(self.constant_pools))

        print('{')
        for item in self.fields:
            print(item.to_string(self.constant_pools))

        print()
        for item in self.methods:
            print(item.to_string(self.constant_pools))
        print()
        print('}')
        for attribute in self.attributes:
            print(attribute.to_string(self.constant_pools))

def parse_class_bytes(bytes):
    java_class = JavaClassFile()
    java_class.parse_bytes(bytes)
    java_class.print()


# Attributes
class AttributeConstantValue(Attributes):
    name = 'ConstantValue'
    def parse_info(self):
        self.constantvalue_index = self.parse_u16int()

    def to_string(self, pools):
        return pools[self.constantvalue_index - 1]

class AttributeCode(Attributes):
    name = 'Code'
    def parse_info(self):
        self.max_stack = self.parse_uint()
        self.max_locals = self.parse_uint()
        self.code_length = self.parse_uint(4)
        next_position = self._position + self.code_length
        self.code = self.bytes[self._position:next_position]
        self._position = next_position
        self.exception_table_length = self.parse_uint()
        self.exception_table = [{'start_pc': self.parse_uint(),\
                                 'end_pc': self.parse_uint(),\
                                 'handler_pc': self.parse_uint(),\
                                 'catch_type': self.parse_uint()} for x in range(self.exception_table_length)]
        self.attributes_count = self.parse_uint()
        self.attributes = [Attributes.generate_attribute(self) for x in range(self.attributes_count)]
        self.parse_code()

    def parse_code(self):
        pc = 0
        self.codes = []
        while pc < len(self.code):
            opcode = self.code[pc]
            clazz = _opcode_2_clazz[opcode]
            pc += 1
            instruction = clazz()
            self.codes.append(instruction)
            if hasattr(instruction, 'parse_info') and callable(instruction.parse_info):
                pc = instruction.parse_info(self.code, pc)
        
    def to_string(self, pools):
        result = 'Code: \n'
        for exception in self.exception_table:
            result += f'start_pc: {exception.start_pc}, end_pc: {exception.end_pc}, handler_pc: {exception.handler_pc}, catch_type: {exception.catch_type}\n'
        result += f'max statck: {self.max_stack}, max locals: {self.max_locals}\n'
        for code in self.codes:
            if hasattr(code, 'to_string')  and callable(code.to_string):
                result += code.to_string(pools) + '\n'
            else:
                result += code.__class__.instruction_name + '\n'
                
        for attribute in self.attributes:
            result += attribute.to_string(pools) + '\n'
        result += 'END CODE'
        return result

class AttributeStackMapTable(Attributes):
    name = 'StackMapTable'
    def parse_info(self):
        self.number_of_entries = self.parse_u16int()
        self.entries = [Frame.generate_frame(self) for index in range(self.number_of_entries)]
        
class AttributeExceptions(Attributes):
    name = 'Exceptions'
    def parse_info(self):
        self.number_of_exceptions = self.parse_u16int()
        self.exception_index_table = [self.parse_u16int for index in range(self.number_of_exceptions)]

class AttributeBoostrapMethods(Attributes):
    name = 'BootstrapMethods'

    def parse_info(self):
        self.num_bootstrap_methods = self.parse_u16int()
        self.bootstrap_methods = [self._parse_bootstrap_methods() for index in range(self.num_bootstrap_methods)]

    def _parse_bootstrap_methods(self):
        ref = self.parse_u16int()
        num_arguments = self.parse_u16int()
        arguments = [self.parse_u16int() for index in range(num_arguments)]
        return {'bootstrap_method_ref': ref, 'num_bootstrap_arguments': num_arguments, 'bootstrap_arguments': arguments}

    def to_string(self, pools):
        result = 'BootstrapMethods:\n'
        for index, val in enumerate(self.bootstrap_methods):
            result += str(index) + ': #' + str(val['bootstrap_method_ref']) + ' ' + pools[val['bootstrap_method_ref'] - 1].comment(pools) + '\n'
            result += 'Method arguments:\n'.rjust(20)
            for value in val['bootstrap_arguments']:
                result += f"#{value} ".rjust(9) + pools[value - 1].comment(pools) + '\n'
        return result

class AttributeInnerClasses(Attributes):
    name = 'InnerClasses'

    def parse_info(self):
        self.number_of_classes = self.parse_u16int()
        self.classes = [{'inner_class_info_index': self.parse_u16int(), \
                         'outer_class_info_index': self.parse_u16int(), \
                         'inner_name_index': self.parse_u16int(), \
                         'inner_classes_access_flags': self.parse_u16int()} for index in range(self.number_of_classes)]

    def to_string(self, pools):
        result = 'InnerClasses:\n'
        for item in self.classes:
            result += "#".rjust(10) + str(item['inner_name_index']) + ' = #' + str(item['inner_class_info_index']) + ' of #' + str(item['outer_class_info_index']) + '\n'
        return result

class AttributeEnclosingMethod(Attributes):
    name = 'EnclosingMethod'

    def parse_info(self):
        self.class_index = self.parse_u16int()
        self.method_index = self.parse_u16int()

class AttributeSynthetic(Attributes):
    name = 'Synthetic'

class AttributeSignature(Attributes):
    name = 'Signature'
    def parse_info(self):
        self.signature_index = self.parse_u16int()

class AttributeRuntimeVisibleAnnotations(Attributes):
    name = 'RuntimeVisibleAnnotations'

    def parse_info(self):
        self.num_annotations = self.parse_u16int()
        self.annotations = [Annotation(self) for index in range(self.num_annotations)]

class AttributeRuntimeInvisibleAnnotations(Attributes):
    name = 'RuntimeInvisibleAnnotations'

    def parse_info(self):
        self.num_annotations = self.parse_u16int()
        self.annotations = [Annotation(self) for index in range(self.num_annotations)]

class AttributeRuntimeVisibleParameterAnnotations(Attributes):
    name = 'RuntimeVisibleParamterAnnotations'

    def parse_info(self):
        self.num_parameters = self.bytes[self._position]
        self._position += 1
        self.parameter_annotations = [self._parse_parameter_annotation() for item in range(self.num_parameters)]

    def _parse_parameter_annotation(self):
        num_annotations = self.parse_u16int()
        annotations = [Annotation(self) for index in range(num_annotations)]
        return {'num_annotations': num_annotations, 'annotations': annotations}

class AttributeRuntimeInvisibleParameterAnnotations(AttributeRuntimeVisibleParameterAnnotations):
    name = 'RuntimeInvisibleParameterAnnotations'

class AttributeRuntimeVisibleTypeAnnotations(Attributes):
    name = 'RuntimeVisibleTypeAnnotations'

    def parse_info(self):
        self.num_annotations = self.parse_u16int()
        self.annotations = [TypeAnnotation(self) for x in range(self.num_annotations)]

class AttributeRuntimeInVisibleTypeAnnotations(AttributeRuntimeVisibleTypeAnnotations):
    name = 'RuntimeInvisibleTypeAnnotations'

class AttributeAnnotationDefault(Attributes):
    name = 'AnnotationDefault'

    def parse_info(self):
        self.default_value = ElementValue.generate_element_value(self)

class AttributeMethodParameters(Attributes):
    name = 'MethodParameters'

    def parse_info(self):
        self.parameters_count = self.parse_u16int()
        self.parameters = [{'name_index': self.parse_u16int(), \
                            'access_flags': self.parse_u16int()} \
                           for index in range(self.parameters_count)]

class AttributeSourceFile(Attributes):
    name = 'SourceFile'

    def parse_info(self):
        self.sourcefile_index = self.parse_u16int()

    def to_string(self, pools):
        return 'SourceFile: ' + pools[self.sourcefile_index - 1].info

class AttributeSourceDebugExtension(Attributes):
    name = 'SourceDebugExtension'

class AttributeLineNumberTable(Attributes):
    name = 'LineNumberTable'

    def parse_info(self):
        self.line_number_table_length = self.parse_u16int()
        self.line_number_table = [{'start_pc': self.parse_u16int(), 'line_number': self.parse_u16int()} for index in range(self.line_number_table_length)]

    def to_string(self, pools):
        result = AttributeLineNumberTable.name + ':\n'
        for item in self.line_number_table:
            result += f"start_pc: {item['start_pc']}, line_number: {item['line_number']}\n"
        return result

class AttributeLocalVariableTable(Attributes):
    name = 'LocalVariableTable'

    def parse_info(self):
        self.local_variable_table_length = self.parse_u16int()
        self.local_variable_table = [{'start_pc': self.parse_u16int(), \
                                      'length': self.parse_u16int(), \
                                      'name_index': self.parse_u16int(), \
                                      'descriptor_index': self.parse_u16int(), \
                                      'index': self.parse_u16int()} for index in range(self.local_variable_table_length)]

    def to_string(self, pools):
        result = AttributeLocalVariableTable.name + ':\n'
        for item in self.local_variable_table:
            variable_name = pools[item['name_index'] - 1].info
            variable_desc = pools[item['descriptor_index'] - 1].info
            result += f"start_pc: {item['start_pc']}, length: {item['length']}, name: {variable_name}, desc: {variable_desc}, index: {item['index']}\n"
        return result

class AttributeLocalVariableTypeTable(Attributes):
    name = 'LocalVariableTypeTable'

    def parse_info(self):
        self.local_variable_type_table_length = self.parse_u16int()
        self.local_variable_type_table = [{'start_pc': self.parse_u16int(), \
                                           'length': self.parse_u16int(), \
                                           'name_index': self.parse_u16int(), \
                                           'signature_index': self.parse_u16int(), \
                                           'index': self.parse_u16int()} for index in self.local_variable_type_table_length]

class AttributeDeprecated(Attributes):
    name = 'Deprecated'
    

# Frame 部分
class Frame:
    def __init__(self, frame_type):
        self.frame_type = frame_type

    @staticmethod
    def generate_frame(like_java_class):
        frame_type = like_java_class.bytes[like_java_class._position]
        like_java_class._position += 1

        frames = [FrameSame, FrameSameLocals1StackItem, FrameSameLocals1StackItemExtended,
                  FrameChop, FrameSameExtended, FrameAppend, FrameFull]
        for frame in frames:
            if frame.type_match(frame_type):
                result = frame(frame_type)
                if hasattr(result, 'parse') and callable(result.parse):
                    result.parse(like_java_class)
                break
        else:
            raise Exception('not found valid frame')
        

class FrameSame(Frame):
    @staticmethod
    def type_match(frame_type):
        return frame_type >= 0 and frame_type <= 63
        

class FrameSameLocals1StackItem(Frame):
    @staticmethod
    def type_match(frame_type):
        return frame_type >= 64 and frame_type <= 127

    def parse(self, like_java_class):
        self.stack = VerificationTypeInfo.generate_verificaction_type_info(like_java_class)

class FrameSameLocals1StackItemExtended:
    @staticmethod
    def type_match(frame_type):
        return frame_type == 247

    def parse(self, like_java_class):
        self.offset_delta = like_java_class.parse_u16int()
        self.stack = VerificationTypeInfo.generate_verification_type_info(like_java_class)

class FrameChop:
    @staticmethod
    def type_match(frame_type):
        return frame_type >= 248 and frame_type <= 250

    def parse(like_java_class):
        self.offset_delta = like_java_class.parse_u16int()

class FrameSameExtended:
    @staticmethod
    def type_match(frame_type):
        return frame_type == 251

    def parse(self, like_java_class):
        self.offset_delta = like_java_class.parse_u16int()

class FrameAppend:
    @staticmethod
    def type_match(frame_type):
        return frame_type >= 252 and frame_type <= 254

    def parse(self, like_java_class):
        self.offset_delta = like_java_class.parse_u16int()
        self.locals = [VerificationTypeInfo.generate_verification_type_info(like_java_class) for index in range(self.frame_type -251)]

class FrameFull:
    @staticmethod
    def type_match(frame_type):
        return frame_type == 255

    def parse(self, like_java_class):
        self.offset_delta = like_java_class.parse_u16int()
        self.number_of_locals = like_java_class.parse_u16int()
        self.number_of_locals = like_java_class.parse_u16int()
        self.locals = [VerificationTypeInfo.generate_verification_type_info(like_java_class) for index in range(self.number_of_locals)]
        self.number_of_stack_items = like_java_class.parse_u16int()
        self.stack = [VerificationTypeInfo.generate_verification_type_info(like_java_class) for index in range(self.number_of_stack_items)]
    

# verification_type_info
class VerificationTypeInfo:
    def parse_info(self, like_java_class):
        raise NotImplementedError()

    @staticmethod
    def generate_verificaction_type_info(like_java_class):
        verification_types = [VerificationTypeInfoTop, VerificationTypeInfoInteger, VerificationTypeInfoFloat,
                              VerificationTypeInfoDouble, VerificationTypeInfoLong, VerificationTypeInfoNull,
                              VerificationTypeInfoUninitializedThis, VerificationTypeInfoObject, VerificationTypeInfoUninitialized]
        tag = like_java_class.bytes[like_java_class._position]
        result = verification_types[tag]()
        if hasattr(result, 'parse') and callable(result.parse):
            result.parse(like_java_class)
        return result
    
    
class VerificationTypeInfoTop(VerificationTypeInfo):
    tag = 0

class VerificationTypeInfoInteger(VerificationTypeInfo):
    tag = 1

class VerificationTypeInfoFloat(VerificationTypeInfo):
    tag = 2

# Two cases: Operand Stack or Local Variable Array    
class VerificationTypeInfoLong(VerificationTypeInfo):
    tag = 4

# Two cases: Operand Stack or Local Variable Array
class VerificationTypeInfoDouble(VerificationTypeInfo):
    tag = 3

class VerificationTypeInfoNull(VerificationTypeInfo):
    tag = 5

class VerificationTypeInfoUninitializedThis(VerificationTypeInfo):
    tag = 6

class VerificationTypeInfoObject(VerificationTypeInfo):
    tag = 7

    def parse(self, like_java_class):
        self.cpool_index = like_java_class.parse_u16int()

class VerificationTypeInfoUninitialized(VerificationTypeInfo):
    tag = 8

    def parse(self, like_java_class):
        self.offset = like_java_class.parse_u16int()


class Annotation:
    def __init__(self, like_java_class):
        self.type_index = like_java_class.parse_u16int()
        self.number_element_value_pairs = like_java_class.parse_u16int()
        self.element_value_pairs = [{'element_name_index': like_java_class.parse_u16int(), \
                                     'value': ElementValue.generate_element_value(like_java_class)}\
                                    for index in range(self.number_element_value_pairs)]

class TypeAnnotation:
    def __init__(self, target_type):
        self.target_type = target_type

    @staticmethod
    def generate_type_annotation(like_java_class):
        target_type = like_java_class.bytes[like_java_class._position]
        like_java_class._position += 1
        type_2_target_type = {
            0x00: TargetTypeParameter, 0x01: TargetTypeParameter, 0x10: TargetSuperType, 0x11: TargetTypeParameterBound,
            0x12: TargetTypeParameterBound, 0x13: TargetEmpty, 0x14: TargetEmpty,
            0x15: TargetEmpty, 0x16: TargetFormalParameter, 0x17: TargetThrows,

            0x40: TargetLocalVar, 0x41: TargetLocalVar, 0x42: TargetCatch,
            0x43: TargetOffset,   0x44: TargetOffset,   0x45: TargetOffset,
            0x46: TargetOffset,   0x47: TargetTypeArgument, 0x48: TargetTypeArgument,
            0x49: TargetTypeArgument, 0x4A: TargetTypeArgument, 0x4B: TargetTypeArgument
        }
        target_type = type_2_target_type[target_type]
        target = target_type(like_java_class)
        
        TODO

class TypePath:
    def __init__(self, like_java_class):
        self.path_length = like_java_class.bytes[like_java_class._position]
        like_java_class._position += 1
        self.path = [TypePath._parse_path(like_java_class) for item in range(self.path_length)]

    @staticmethod
    def _parse_path(like_java_class):
        type_path_kind = like_java_class.bytes[like_java_class._position]
        like_java_class._position += 1
        type_argument_index = like_java_class.bytes[like_java_class._position]
        like_java_class._position += 1
        return {'type_path_kind': type_path_kind, 'type_argument_index': type_argument_index}

class TargetTypeParameter:
    def __init__(self, like_java_class):
        self.type_parameter_index = like_java_class.bytes[like_java_class._position]
        like_java_class._position += 1
        

class TargetSuperType:
    def __init__(self, like_java_class):
        self.super_type_index = like_java_class.parse_u16int()

class TargetTypeParameterBound:
    def __init__(self, like_java_class):
        self.type_parameter_index = like_java_class.bytes[like_java_class._position]
        self.bound_index = like_java_class.bytes[like_java_class._position + 1]
        like_java_class._position += 2

class TargetEmpty:
    def __init__(self, like_java_class):
        pass

class TargetFormalParameter:
    def __init__(self, like_java_class):
        self.formal_parameter_index = like_java_class.bytes[like_java_class._position]
        like_java_class._position += 1

class TargetThrows:
    def __init__(self, like_java_class):
        self.throws_type_index = like_java_class.parse_u16int()

class TargetLocalVar:
    def __init__(self, like_java_class):
        self.table_length = like_java_class.parse_u16int()
        self.table = [{'start_pc': like_java_class.parse_u16int(), \
                       'length': like_java_class.parse_u16int(), \
                       'index': like_java_class.parse_u16int()} \
                      for index in range(self.table_length)]

class TargetCatch:
    def __init__(self, like_java_class):
        self.exception_table_index = like_java_class.parse_u16int()

class TargetOffset:
    def __init__(self, like_java_class):
        self.offset = like_java_class.parse_u16int()

class TargetTypeArgument:
    def __init__(self, like_java_class):
        self.offset = like_java_class.parse_u16int()
        self.type_argument_index = like_java_class.bytes[like_java_class._position]
        like_java_class._position += 1


class ElementValue:
    def __init__(self, tag):
        self.tag = tag

    @staticmethod
    def generate_element_value(like_java_class):
        tag = like_java_class[like_java_class._position]
        like_java_class._position += 1
        elemnt_types = [ElementValuePrimite, ElementValueEnum, ElementValueClass, ElementValueAnnotationType, ElementValueArrayType]
        for item in elemnt_types:
            if item.tags == tag or tag in item.tags:
                result = item(tag)
                result.parse(like_java_class)
                return result
        else:
            raise Exception('not found valid element value')

class ElementValuePrimite(ElementValue):
    tags = {'B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z', 's'}

    def parse(like_java_class):
        self.value = {'const_value_index': like_java_class.parse_u16int()}

class ElementValueEnum(ElementValue):
    tags = 'e'

    def parse(like_java_class):
        self.value = {'enum_const_value':
                      {'type_name_index': like_java_class.parse_u16int(),
                       'const_name_index': like_java_class.parse_u16int()}}

class ElementValueClass(ElementValue):
    tags = 'c'

    def parse(like_java_class):
        self.value = {'class_info_index': like_java_class.parse_u16int()}

class ElementValueAnnotationType(ElementValue):
    tags = '@'
    def parse(like_java_class):
        self.value = {'annotation_value': Annotation(like_java_class)}

class ElementValueArrayType(ElementValue):
    tags = '['
    def parse(like_java_class):
        num_values = self.parse_u16int()
        self.value = {'array_value':
                      {'num_values': num_values,
                       'values': [ElementValue.generate_element_value(like_java_class) for index in range(num_values)]}}


_opcode_2_clazz = {}
def Instructions(clazz):
    _opcode_2_clazz[clazz.opcode] = clazz
    clazz.instruction_name = clazz.__name__[len('Instruction_'):].upper()
    return clazz


@Instructions
class Instruction_aaload:
    opcode = 0x32  # 50

@Instructions
class Instruction_aastore:
    opcode = 0x53 # 83

@Instructions
class Instruction_aconst_null:
    opcode = 0x1  # 1

@Instructions
class Instruction_aload:
    opcode = 0x19 # 25

@Instructions
class Instruction_aload_0:
    opcode = 0x2a # 42

@Instructions
class Instruction_aload_1:
    opcode = 0x2b # 43

@Instructions
class Instruction_aload_2:
    opcode = 0x2c # 44

@Instructions
class Instruction_aload_3:
    opcode = 0x2d # 45

@Instructions
class Instruction_anewarray:
    opcode = 0xbd # 189

@Instructions
class Instruction_areturn:
    opcode = 0xb0 # 176

@Instructions
class Instruction_arraylength:
    opcode = 0xbe # 190

@Instructions
class Instruction_astore:
    opcode = 0x3a # 58

@Instructions
class Instruction_atore_0:
    opcode = 0x4b # 75

@Instructions
class Instruction_astore_1:
    opcode = 0x4c # 76

@Instructions
class Instruction_astore_2:
    opcode = 0x4d #77

@Instructions
class Instruction_astore_3:
    opcode = 0x4e #78

@Instructions
class Instruction_athrow:
    opcode = 0xbf # 191

@Instructions
class Instruction_baload:
    """ Load byte or boolean from array  """
    opcode = 0x33 # 51

@Instructions
class Instruction_bastore:
    opcode = 0x54 # 84

@Instructions
class Instruction_bipush:
    opcode = 0x10 # 10

@Instructions
class Instruction_caload:
    """ Load char from array  """
    opcode = 0x34 # 52

@Instructions
class Instruction_castore:
    """ Store into char array  """
    opcode = 0x55

@Instructions
class Instruction_checkcase:
    opcode = 0xc0 # 192

@Instructions
class Instruction_d2f:
    opcode = 0x90 # 144

@Instructions
class Instruction_d2i:
    opcode = 0x8e # 142

@Instructions
class Instruction_d2l:
    opcode = 0x8f # 143

@Instructions
class Instruction_dadd:
    opcode = 0x63 # 99

@Instructions
class Instruction_daload:
    opcode = 0x31 # 49

@Instructions
class Instruction_dstore:
    opcode = 0x52 # 82

@Instructions
class Instruction_dcmpg:
    opcode = 0x98 # 152

@Instructions
class Instruction_dcmpl:
    opcode = 0x97 # 151

@Instructions
class Instruction_dconst_0:
    """ push double 0.0 onto the operand stack  """
    opcode = 0xe # 14

@Instructions
class Instruction_dconst_1:
    """ push double 1.0 onto the operand stack  """
    opcode = 0xf # 15

@Instructions
class Instruction_ddiv:
    opcode = 0x6f # 111

@Instructions
class Instruction_dload:
    opcode = 0x18 # 24

@Instructions
class Instruction_dload_0:
    opcode = 0x26 # 38

@Instructions
class Instruction_dload_1:
    opcode = 0x27 # 39

@Instructions
class Instruction_dload_2:
    opcode = 0x28 # 40

@Instructions
class Instruction_dload_3:
    opcode = 0x29 # 41

@Instructions
class Instruction_dmul:
    opcode = 0x6b # 107

@Instructions
class Instruction_dneg:
    opcode = 0x77 # 119

@Instructions
class Instruction_drem:
    opcode = 0x73 # 115

@Instructions
class Instruction_dreturn:
    opcode = 0xaf # 175

@Instructions
class Instruction_dstore:
    opcode = 0x39 # 57

@Instructions
class Instruction_dstore_0:
    opcode = 0x47 # 71

@Instructions
class Instruction_dstore_1:
    opcode = 0x48 # 72

@Instructions
class Instruction_dstore_2:
    opcode = 0x49 # 73

@Instructions
class Instruction_dstore_3:
    opcode = 0x4a # 74

@Instructions
class Instruction_dsub:
    opcode = 0x67 # 103

@Instructions
class Instruction_dup:
    opcode = 0x59 # 89

@Instructions
class Instruction_dup_x1:
    """ Duplicate the top operand stack value and insert two values down  """
    opcode = 0x5a # 90

@Instructions
class Instruction_dup_x2:
    """ TODO: 很特殊的一种指令  """
    opcode = 0x5b #91

@Instructions
class Instruction_dup2:
    """ TODO: 未完全解析的指令  """
    opcode = 0x5c #92

@Instructions
class Instruction_dup2_x1:
    opcode = 0x5d #93

@Instructions
class Instruction_dup2_x2:
    opcode = 0x5e # 94

@Instructions
class Instruction_f2d:
    opcode = 0x8d # 141

@Instructions
class Instruction_f2i:
    opcode = 0x8b # 139

@Instructions
class Instruction_f2l:
    opcode = 0x8c # 140

@Instructions
class Instruction_fadd:
    opcode = 0x62 # 98

@Instructions
class Instruction_faload:
    opcode = 0x30 # 48

@Instructions
class Instruction_fastore:
    opcode = 0x51 #81

@Instructions
class Instruction_fcmpg:
    opcode = 0x96 # 150

@Instructions
class Instruction_fcmpl:
    opcode = 0x95 # 149

@Instructions
class Instruction_fconst_0:
    opcode = 0xb # 11

@Instructions
class Instruction_fconst_1:
    opcode = 0xc # 12

@Instructions
class Instruction_fconst_2:
    """ push float 2.0 onto operand stack  """
    opcode = 0xd # 13

@Instructions
class Instruction_fdiv:
    opcode = 0x6e # 110

@Instructions
class Instruction_fload:
    opcode = 0x17 # 23

@Instructions
class Instruction_fload_0:
    opcode = 0x22 # 34

@Instructions
class Instruction_fload_1:
    opcode = 0x23 # 35

@Instructions
class Instruction_fload_2:
    opcode = 0x24 # 36

@Instructions
class Instruction_fload_3:
    opcode = 0x25 # 37

@Instructions
class Instruction_fmul:
    opcode = 0x6a # 106

@Instructions
class Instruction_fneg:
    opcode = 0x76 # 118

@Instructions
class Instruction_frem:
    opcode = 0x72 # 114

@Instructions
class Instruction_freturn:
    opcode = 0xae # 174

@Instructions
class Instruction_fstore:
    opcode = 0x38 # 56

@Instructions
class Instruction_fstore_0:
    opcode = 0x43 # 67

@Instructions
class Instruction_fstore_1:
    opcode = 0x44 # 68

@Instructions
class Instructions_fstore_2:
    opcode = 0x45 # 69

@Instructions
class Instructions_fstore_3:
    opcode = 0x46 # 70

@Instructions
class Instruction_fsub:
    opcode = 0x66 # 102

@Instructions
class Instruction_getfield:
    opcode = 0xb4 # 180

@Instructions
class Instruction_getstatic:
    opcode = 0xb2 # 178

    def parse_info(self, codes, start_index):
        self.cp_index = int.from_bytes(codes[start_index : start_index + 2], byteorder='big')
        return start_index + 2

    def to_string(self, pools):
        value = pools[self.cp_index - 1].comment(pools)
        return f"GETSTATIC \t#{self.cp_index} //{value}"
    
@Instructions
class Instruction_goto:
    opcode = 0xa7 # 167

@Instructions
class Instruction_goto_w:
    opcode = 0xc8 # 200

@Instructions
class Instruction_i2b:
    opcode = 0x91 # 145

@Instructions
class Instruction_i2c:
    opcode = 0x92 # 146

@Instructions
class Instruction_i2d:
    opcode = 0x87 # 135

@Instructions
class Instruction_i2f:
    opcode = 0x86 # 134

@Instructions
class Instruction_i2l:
    opcode = 0x85 # 133

@Instructions
class Instruction_i2s:
    opcode = 0x93 # 147

@Instructions
class Instruction_iadd:
    opcode = 0x60 # 96

@Instructions
class Instruction_iaload:
    opcode = 0x2e # 46

@Instructions
class Instruction_iand:
    """ Boolean AND int  """
    opcode = 0x7e # 126

@Instructions
class Instruction_iastore:
    opcode = 0x4f # 79

@Instructions
class Instruction_iconst_m1:
    opcode = 0x2 # 2

@Instructions
class Instruction_iconst_0:
    opcode = 0x3

@Instructions
class Instruction_iconst_1:
    opcode = 0x4

@Instructions
class Instruction_iconst_2:
    opcode = 0x5

@Instructions
class Instruction_iconst_3:
    opcode = 0x6

@Instructions
class Instruction_iconst_4:
    opcode = 0x7

@Instructions
class Instruction_iconst_5:
    opcode = 0x8

@Instructions
class Instruction_idiv:
    opcode = 0x6c # 108

@Instructions
class Instruction_if_acmpeq:
    opcode = 0xa5 # 165

@Instructions
class Instruction_if_acmpne:
    opcode = 0xa6 # 166

@Instructions
class Instruction_if_icmpeq:
    opcode = 0x9f # 159

@Instructions
class Instruction_if_icmpne:
    opcode = 0xa0 # 160

@Instructions
class Instruction_if_icmplt:
    opcode = 0xa1 # 161

@Instructions
class Instruction_if_icmpge:
    opcode = 0xa2 # 162

@Instructions
class Instruction_if_icmpgt:
    opcode = 0xa3 # 163

@Instructions
class Instruction_if_icmple:
    opcode = 0xa4 # 164

@Instructions
class Instruction_ifeq:
    opcode = 0x99 # 153

@Instructions
class Instruction_ifne:
    opcode = 0x9a # 154

@Instructions
class Instruction_iflt:
    opcode = 0x9b # 155

@Instructions
class Instruction_ifge:
    opcode = 0x9c # 156

@Instructions
class Instruction_ifgt:
    opcode = 0x9d # 157

@Instructions
class Instruction_ifle:
    opcode = 0x9e # 158

@Instructions
class Instruction_ifnonnull:
    opcode = 0xc7 # 199

@Instructions
class Instruction_ifnull:
    opcode = 0xc6 # 198

@Instructions
class Instruction_iinc:
    """ Increment local variable by constant  """
    opcode = 0x84 # 132

@Instructions
class Instruction_iload:
    opcode = 0x15 # 21

@Instructions
class Instruction_iload_0:
    opcode = 0x1a # 26

@Instructions
class Instruction_iload_1:
    opcode = 0x1b # 27

@Instructions
class Instruction_iload_2:
    opcode = 0x1c # 28

@Instructions
class Instruction_iload_3:
    opcode = 0x1d # 29

@Instructions
class Instruction_imul:
    opcode = 0x68 # 104

@Instructions
class Instruction_ineg:
    opcode = 0x74 # 116

@Instructions
class Instruction_instanceof:
    opcode = 0xc1 # 193

@Instructions
class Instruction_invokedynamic:
    opcode = 0xba # 186

@Instructions
class Instruction_invokeinterface:
    opcode = 0xb9 # 185

    def parse_info(self, bytes, start_index):
        self.index = int.from_bytes(bytes[start_index: start_index + 2], byteorder='big')
        return start_index + 2

    def to_string(self, pools):
        value = pools[self.index - 1].comment(pools)
        return f"{self.invoke_type()} \t#{self.index} //{value}"

    def invoke_type(self):
        return 'INVOKE-INTERFACE'

@Instructions
class Instruction_invokespecial(Instruction_invokeinterface):
    opcode = 0xb7 # 183

    def invoke_type(self):
        return 'INVOKE-SPECIAL'

@Instructions
class Instruction_invokestatic(Instruction_invokeinterface):
    opcode = 0xb8 # 184

    def invoke_type(self):
        return 'INVOKE-STATIC'

@Instructions
class Instruction_invokevirtual(Instruction_invokeinterface):
    opcode = 0xb6 # 182

    def invoke_type(self):
        return 'INVOKE-VIRTUAL'


@Instructions
class Instruction_ior:
    opcode = 0x80 # 128

@Instructions
class Instruction_irem:
    opcode = 0x70 # 112

@Instructions
class Instruction_ireturn:
    opcode = 0xac # 172

@Instructions
class Instruction_ishl:
    opcode = 0x78 # 120

@Instructions
class Instruction_ishr:
    opcode = 0x7a # 122

@Instructions
class Instruction_istore:
    opcode = 0x36 # 54

@Instructions
class Instruction_istore_0:
    opcode = 0x3b # 59

@Instructions
class Instruction_istore_1:
    opcode = 0x3c # 60

@Instructions
class Instruction_istore_2:
    opcode = 0x3d # 61

@Instructions
class Instruction_istore_3:
    opcode = 0x3e # 62

@Instructions
class Instruction_isub:
    opcode = 0x64 # 100

@Instructions
class Instruction_iushr:
    opcode = 0x7c # 124

@Instructions
class Instruction_ixor:
    opcode = 0x82 # 130

@Instructions
class Instruction_jsr:
    opcode = 0xa8 # 168

@Instructions
class Instruction_jsr_w:
    opcode = 0xc9 # 201

@Instructions
class Instruction_l2d:
    opcode = 0x8a # 138

@Instructions
class Instruction_l2f:
    opcode = 0x89 # 137

@Instructions
class Instruction_l2i:
    opcode = 0x88 # 136

@Instructions
class Instruction_ladd:
    opcode = 0x61 # 97

@Instructions
class Instruction_laload:
    opcode = 0x2f # 47

@Instructions
class Instruction_land:
    opcode = 0x7f # 127

@Instructions
class Instruction_lastore:
    opcode = 0x50 # 80

@Instructions
class Instruction_lcmp:
    opcode = 0x94 # 148

@Instructions
class Instruction_lconst_0:
    opcode = 0x9 # 9

@Instructions
class Instruction_lconst_1:
    opcode = 0xa # 10

@Instructions
class Instruction_ldc:
    opcode = 0x12 # 18

    def parse_info(self, bytes, start_index):
        self.index = bytes[start_index]
        return start_index + 1

    def to_string(self, pools):
        value = pools[self.index - 1].comment(pools)
        return f"LDC \t\t#{self.index} //{value}"

@Instructions
class Instruction_ldc_w:
    opcode = 0x13 # 19

@Instructions
class Instruction_ldc2_w:
    opcode = 0x14 # 20

@Instructions
class Instruction_ldiv:
    opcode = 0x6d

@Instructions
class Instruction_lload:
    opcode = 0x16 # 22

@Instructions
class Instruction_lload_0:
    opcode = 0x1e # 30

@Instructions
class Instruction_lload_1:
    opcode = 0x1f # 31

@Instructions
class Instruction_lload_2:
    opcode = 0x20 # 32

@Instructions
class Instruction_lload_3:
    opcode = 0x21 # 33

@Instructions
class Instruction_lmul:
    opcode = 0x69 # 105

@Instructions
class Instruction_lneg:
    opcode = 0x75 # 117

@Instructions
class Instruction_lookupswitch:
    opcode = 0xab # 171

@Instructions
class Instruction_lor:
    opcode = 0x81 # 129

@Instructions
class Instruction_lrem:
    opcode = 0x71 # 113

@Instructions
class Instruction_lreturn:
    opcode = 0xad # 173

@Instructions
class Instruction_lshl:
    opcode = 0x79 # 121

@Instructions
class Instruction_lshr:
    opcode = 0x7b # 123

@Instructions
class Instruction_lstore:
    opcode = 0x37 # 55

@Instructions
class Instruction_lstore_0:
    opcode = 0x3f # 63

@Instructions
class Instruction_lstore_1:
    opcode = 0x40 # 64

@Instructions
class Instruction_lstore_2:
    opcode = 0x41 # 65

@Instructions
class Instruction_lstore_3:
    opcode = 0x42 # 66

@Instructions
class Instruction_lsub:
    opcode = 0x65 # 101

@Instructions
class Instruction_lushr:
    opcode = 0x7d # 125

@Instructions
class Instruction_lxor:
    opcode = 0x83 # 131

@Instructions
class Instruction_monitorenter:
    opcode = 0xc2 # 194

@Instructions
class Instruction_monitorexit:
    opcode = 0xc3 # 195

@Instructions
class Instruction_multianewarray:
    opcode = 0xc5 # 197

@Instructions
class Instruction_new:
    opcode = 0xbb # 187

@Instructions
class Instruction_newarray:
    opcode = 0xbc # 188

@Instructions
class Instruction_nop:
    opcode = 0x0 # 0

@Instructions
class Instruction_pop:
    opcode = 0x57 # 87

@Instructions
class Instruction_pop2:
    opcode = 0x58 # 88

@Instructions
class Instruction_putfield:
    opcode = 0xb5 # 181

@Instructions
class Instruction_putstatic:
    opcode = 0xb3 # 179

@Instructions
class Instruction_ret:
    opcode = 0xa9 # 169

@Instructions
class Instruction_return:
    opcode = 0xb1 # 177

@Instructions
class Instruction_saload:
    opcode = 0x35 # 53

@Instructions
class Instruction_sastore:
    opcode = 0x56 # 86

@Instructions
class Instruction_sipush:
    opcode = 0x11 # 17

@Instructions
class Instruction_swap:
    opcode = 0x5f # 95

@Instructions
class Instruction_tableswitch:
    opcode = 0xaa #170

@Instructions
class Instruction_wide:
    opcode = 0xc4 # 196


if __name__ == '__main__':
    file_name = sys.argv[1]
    print(f'file_name is: {file_name}')
    filebuffer = None
    with open(file_name, 'rb') as file:
        filebuffer = file.read()

    if filebuffer:
        parse_class_bytes(filebuffer)
    else:
        print('not valid file')
